package br.uece.comp.paa.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.DataFormatException;

import br.uece.comp.paa.entity.grafo.Grafo;
import br.uece.comp.paa.entity.grafo.Vertice;

/**
 * Classe responsável pelo acesso ao arquivo e construção de uma instância de
 * grafo de acordo com os dados contidos no arquivo.
 */
public class LeitorArquivo {

	/** Buffer para leitura do arquivo. */
	private BufferedReader bufferReader;

	/**
	 * Construi uma instância do leitor.
	 * 
	 * @param file
	 *            Arquivo ao qual se deseja ler o grafo.
	 */
	public LeitorArquivo(File file) {
		try {
			bufferReader = new BufferedReader(new FileReader(file));

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Efetua a leitura no arquivo e a construção do Grafo.
	 * 
	 * @return O grafo baseado nos dados do arquivo.
	 * @throws DataFormatException
	 *             é disparado caso o formato do arquivo esteja incorreto.
	 */
	public Grafo obterGrafo() throws DataFormatException, IOException {
		List<Vertice> vertices = new LinkedList<Vertice>();
		List<Double> numeros = new LinkedList<Double>();
		String linha;
		double qtdVertices, tipoGrafo, qtdArestasSimetricas = 0;

		// TRATANDO O CABEÇALHO
		linha = bufferReader.readLine() + '\n';
		numeros = capturarNumerosEmString(linha);

		if (numeros.size() < 2) {
			throw new DataFormatException("O arquivo não segue o formato especificado. Linha 1!");
		} else {
			qtdVertices = numeros.get(0);
			tipoGrafo = numeros.get(1);

			if (tipoGrafo == Grafo.SIMETRICO) {
				if (numeros.size() < 3) {
					throw new DataFormatException(
							"O arquivo não segue o formato especificado. Linha 1!");
				} else {
					qtdArestasSimetricas = numeros.get(2);
				}
			}
		}

		// OBTENDO OS VERTICES
		Vertice v;
		if(tipoGrafo == Grafo.EUCLIDIANO) {
		for (int i = 0; i < qtdVertices; i++) {
			linha = bufferReader.readLine() + '\n';

			if (linha == null) {
				throw new DataFormatException("Não foram especificados todos os vértices. Linha "
						+ (i + 2) + "!");
			} else {

				numeros = capturarNumerosEmString(linha);

				if (numeros.size() < 3) {
					throw new DataFormatException(
							"O arquivo não segue o formato especificado. Linha " + (i + 2) + "!");
				} else {
					v = new Vertice(numeros.get(0), numeros.get(1), numeros.get(2));
					vertices.add(v);
				}
			}
		}

		
		} else {
			for (int i = 0; i < qtdVertices; i++) {
				v = new Vertice(i+1, 0, 0);
				vertices.add(v);
			}
		}
			Grafo grafo = new Grafo(vertices.toArray(new Vertice[1]), (int) tipoGrafo);
		// SE GRAFO FOR SIMETRICO, OBTER VALOR DAS ARESTAS.
		if (tipoGrafo == Grafo.SIMETRICO) {
			int vertice1, vertice2, custo;

			for (int i = 0; i < qtdArestasSimetricas; i++) {
				linha = bufferReader.readLine() + '\n';

				if (linha == null) {
					throw new DataFormatException(
							"Não foram especificados todos as arestas. Linha " + (qtdVertices + i + 2) + "!");
				} else {

					numeros = capturarNumerosEmString(linha);

					if (numeros.size() < 3) {
						throw new DataFormatException(
								"O arquivo não segue o formato especificado. Linha " + (qtdVertices + i + 2)
										+ "!");
					} else {
						vertice1 =numeros.get(0).intValue();
						vertice2 = numeros.get(1).intValue();
						custo = numeros.get(2).intValue();

						grafo.setCusto(vertice1-1, vertice2-1, custo);
						grafo.setCusto(vertice2-1, vertice1-1, custo);
					}
				}
			}
		} else {
			grafo.calcularMatrizCustoEuclidiano();
		}

		grafo.atualizarGrau();
		return grafo;
	}

	/**
	 * Ler uma string e gera uma lista de números presentes nesta.
	 * 
	 * @param string
	 *            String origem dos números.
	 * @return Lista de Números presentes na string
	 */
	private List<Double> capturarNumerosEmString(String string) {
		List<Double> listaNumeros = new ArrayList<Double>();
		String numero = "";
		char caracter;

		for (int i = 0; i < string.length(); i++) {
			caracter = string.charAt(i);

			if (caracter == '\t' || caracter == ' ' || caracter == '\r' || caracter == '\n'
					|| caracter == ',') {
				if (numero.length() != 0) {
					listaNumeros.add(new Double(numero));
					numero = "";
				}
			} else {
				numero += caracter;
			}
		}
		return listaNumeros;
	}
}
